﻿using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Sgr.Domain.Uow;
using Sgr.EventBus.RelayBoxs.Domain;

namespace Sgr.EventBus.RelayBoxs.BackGroundTasks
{
    public class OutboxMessageCleanupHandle : ICleanupHandle
    {
        private readonly IUnitOfWorkManager _unitOfWorkManager;
        private readonly IOutboxMessageRepository _outboxMessageRepository;
        private readonly RelayBoxOptions _relayBoxOptions;
        private readonly ILogger<OutboxMessageCleanupHandle> _logger;

        public OutboxMessageCleanupHandle(
            IUnitOfWorkManager unitOfWorkManager,
            IOutboxMessageRepository outboxMessageRepository,
            IOptions<RelayBoxOptions> options,
            ILogger<OutboxMessageCleanupHandle> logger)
        {
            _outboxMessageRepository = outboxMessageRepository ?? throw new ArgumentNullException(nameof(outboxMessageRepository));
            _unitOfWorkManager = unitOfWorkManager ?? throw new ArgumentNullException(nameof(unitOfWorkManager));
            _relayBoxOptions = options?.Value ?? throw new ArgumentNullException(nameof(options));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }

        public async Task CleanupAsync(CancellationToken cancellationToken = default)
        {
            await CleanSuccessMessageAsync(cancellationToken);
            await CleanFailedMessageAsync(cancellationToken);
        }

        public async Task CleanSuccessMessageAsync(CancellationToken cancellationToken = default)
        {
            await CleanupMessagesAsync(
                _outboxMessageRepository.CleanupDispatchedMessagesAsync,
                _relayBoxOptions.SuccessMessageRetentionPeriod,
                "已处理", cancellationToken);
        }

        public async Task CleanFailedMessageAsync(CancellationToken cancellationToken = default)
        {
            await CleanupMessagesAsync(
                _outboxMessageRepository.CleanupFailedMessagesAsync,
                _relayBoxOptions.FailedMessageRetentionPeriod,
                "处理失败", cancellationToken);
        }

        private async Task<int> CleanupMessagesAsync(
            Func<DateTime, int, CancellationToken, Task<int>> cleanupFunc,
            int retentionDays,
            string messageType,
            CancellationToken cancellationToken = default)
        {
            _logger.LogInformation("OutboxMessageCleanupHandle 于 {UtcNow} 开始清理{MessageType}的 Outbox 消息", DateTime.UtcNow, messageType);

            int count = 0;
            var maxBatchSize = _relayBoxOptions.OutboxCleanupMaxBatchSize;

            if (maxBatchSize < 10)
                maxBatchSize = 10;

            if (maxBatchSize > 2000)
                maxBatchSize = 2000;

            var maxCycles = _relayBoxOptions.OutboxCleanupMaxCycles;

            if (maxCycles < 5)
                maxCycles = 5;

            if (maxCycles > 500)
                maxCycles = 500;

            var cleanupStartTime = DateTime.UtcNow;

            for (int i = 0; i < maxCycles; i++)
            {
                using var uow = _unitOfWorkManager.Begin();
                try
                {
                    var olderThanUtc = cleanupStartTime.AddDays(-retentionDays);
                    int affected = await cleanupFunc(olderThanUtc, maxBatchSize, cancellationToken);
                    await uow.CommitAsync(cancellationToken);

                    count += affected;
                    if (affected < maxBatchSize) break; // 无更多数据
                }
                catch (Exception ex)
                {
                    await uow.RollbackAsync(cancellationToken);
                    _logger.LogError(ex, "OutboxMessageCleanupHandle 清理{MessageType}失败", messageType);
                    break; // 出错即停止
                }
            }

            _logger.LogInformation("OutboxMessageCleanupHandle {MessageType}清理完成，共计 {Count} 条", messageType, count);
            return count;
        }
    }
}